package com.yugao.fintech.antelope.base.model.module.config.param;

import com.alibaba.fastjson2.JSON;
import com.yugao.fintech.antelope.base.model.enums.ConfigKeyEnum;
import com.yugao.fintech.antelope.base.model.module.tenant.TenantContextHolder;
import com.yugao.fintech.framework.assistant.utils.StringUtils;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import com.yugao.fintech.framework.core.utils.SpringUtils;
import com.yugao.fintech.framework.desensitization.fieldsensitive.FieldSensitiveHandler;
import com.yugao.fintech.framework.validation.ValidationUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;


@Slf4j
@Accessors(chain = true)
public abstract class BaseConfigParam<T extends BaseConfigParam<T>> {
    @Getter
    @Setter
    protected Long tenantId;

    protected static final Map<ConfigKeyEnum, BaseConfigParam<?>> configParamCache = new ConcurrentHashMap<>();

    @PostConstruct
    public void initBaseConfigParam() {
        ConfigKeyEnum configKey = configKey();
        configParamCache.put(configKey, this);
    }

    /**
     * 通过配置key, 将json数据转成实体类
     *
     * 应用场景: configHash为空, 或者 configKey下只有一个configHash
     */
    @SuppressWarnings("unchecked")
    public static <T extends BaseConfigParam<T>> T jsonToObject(String configKey, String json) {
        BaseConfigParam<?> configParam = configParamCache.get(ConfigKeyEnum.ofAndCheck(configKey));
        if (Objects.isNull(configParam)) {
            throw new BizException("尚未定义配置参数实体类");
        }
        configParam.setTenantId(TenantContextHolder.get().getTenantId());
        return (T) configParam.jsonToObject(json);
    }

    /**
     *
     * @apiNote 如果想要转成子类的抽象类 比如转成 {@link StorageParam}, 要先转成 {@link BaseConfigParam}, 再
     *          强转成 {@link StorageParam} eg: <br/>
     *          <blockquote>
     *              BaseConfigParam<?> configParam = BaseConfigParam.jsonToObject('xxx', json);
     *               StorageParam<?> storageParam = (StorageParam<?>) configParam
     *          </blockquote>
     */
    @SuppressWarnings("unchecked")
    public static <T extends BaseConfigParam<T>> T jsonToObject(ConfigKeyEnum configKey, String json) {
        BaseConfigParam<?> configParam = configParamCache.get(configKey);
        if (Objects.isNull(configParam)) {
            return null;
        }
        configParam.setTenantId(TenantContextHolder.get().getTenantId());
        return (T) configParam.jsonToObject(json);
    }

    protected abstract T jsonToObject(String json);

    /**
     * 处理敏感数据 - 数据脱敏
     * @param configKey 配置key
     * @param configValue 配置值
     */
    public static String processSensitiveData(ConfigKeyEnum configKey, String configValue) {
        if (Objects.isNull(configKey)) {
            log.info("configKey 是空, 不进行脱敏处理");
            return configValue;
        }
        BaseConfigParam<?> baseConfigParam = configParamCache.get(configKey);
        if (Objects.isNull(baseConfigParam)) {
            log.info("configKey: {} 未定义 BaseConfigParam 子类, 不进行脱敏处理", configKey.getKey());
            return configValue;
        }
        BaseConfigParam<?> configParam = baseConfigParam.jsonToObject(configValue);
        FieldSensitiveHandler handler = SpringUtils.getBean(FieldSensitiveHandler.class);
        handler.handleField(configParam);
        return JSON.toJSONString(configParam);
    }

    /**
     * 自定义校验
     */
    protected void customValidate() {

    }

    /**
     * 验证
     */
    public static void validate(ConfigKeyEnum configKey, String json) {
        if (StringUtils.isEmpty(json) || Objects.isNull(configKey)) {
            return;
        }

        BaseConfigParam<?> configParam = configParamCache.get(configKey);
        if (Objects.isNull(configParam)) {
            throw new BizException("尚未定义配置参数实体类");
        }
        configParam = configParam.jsonToObject(json);
        ValidationUtils.validateFastThrow(configParam);
        configParam.customValidate();
    }

    public abstract ConfigKeyEnum configKey();
}
